package com.pai4j.pgc.service.behavior;
import com.pai4j.common.constants.MessageConstants;
import com.pai4j.common.enums.RedisKeyEnum;
import com.pai4j.common.enums.community.ResourceBehaviorTypeEnum;
import com.pai4j.common.enums.community.ResourceTypeEnum;
import com.pai4j.common.exception.BusinessException;
import com.pai4j.common.util.DateUtils;
import com.pai4j.common.util.RedisUtil;
import com.pai4j.common.util.SpringContextUtil;
import com.pai4j.domain.community.vo.response.ResourceBehaviorResponseVO;
import com.pai4j.domain.community.vo.response.ResourceBehaviorUserResponseVO;
import com.pai4j.domain.community.vo.response.cc.CTCExperienceDetailVO;
import com.pai4j.domain.community.vo.response.cc.CTCQuestionDetailVO;
import com.pai4j.domain.community.vo.response.cc.CTCVideoDetailVO;
import com.pai4j.domain.vo.dto.LikeCollectMessageDTO;
import com.pai4j.pgc.dao.community.IResourceBehaviorDAO;
import com.pai4j.pgc.entity.community.ResourceBehaviorEntity;
import com.pai4j.pgc.service.community.CCExperienceService;
import com.pai4j.pgc.service.community.CCQuestionService;
import com.pai4j.pgc.service.community.CCVideoService;
import com.pai4j.pgc.service.message.builder.LikeCollectMessageBuilder;
import com.pai4j.pgc.service.queue.UserMessageQueue;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

public abstract class AbstractResourceBehaviorService {
    private String behaviorType;
    public void setBehaviorType(String behaviorType) {
        this.behaviorType = behaviorType;
    }

    /**
     * 资源操作，需要具体实现
     *
     * @param resourceId
     * @param resourceType
     * @param behaviorType
     * @return
     */
    protected abstract boolean behaviorResource(String userId, Long resourceId, String resourceType, String behaviorType);

    /***
     * 取消资源操作
     * @param resourceId
     * @param resourceType
     * @param behaviorType
     * @return
     */
    protected boolean cancelBehaviorResource(String userId, Long resourceId, String resourceType, String behaviorType) {
        throw new BusinessException("不支持的行为");
    }

    /**
     * 互动行为
     * @param userId
     * @param resourceId
     * @param resourceType
     * @return 返回操作成功后的总数
     */
    public long behavior(String userId, Long resourceId, String resourceType) {
        Long behaviorEndCount = null;
        // 资源行为操作
        if (this.behaviorResource(userId, resourceId, resourceType, behaviorType)) {
            // 刷新行为数据缓存
            behaviorEndCount = this.refreshBehaviorCache(resourceId, resourceType, behaviorType, userId, false);
            // 发送站内信，走MQ发送
            this.sendMessage(userId, resourceId, resourceType);
        }
        return behaviorEndCount == null ? 0L : behaviorEndCount;
    }

    /**
     * 取消互动
     *
     * @param resourceId
     * @param resourceType
     * @return 返回操作成功后的总数
     */
    public Long cancelBehavior(String userId, Long resourceId, String resourceType) {
        Long behaviorEndCount = null;
        // 资源行为取消操作
        if (this.cancelBehaviorResource(userId, resourceId, resourceType, behaviorType)) {
            // 刷新行为数据缓存
            behaviorEndCount = refreshBehaviorCache(resourceId, resourceType, behaviorType, userId, true);
        }
        return behaviorEndCount == null ? 0L : behaviorEndCount;
    }

    public String getResourceAuthor(Long resourceId, String resourceType) {
        if (ResourceTypeEnum.CTC_QUESTION.getType().equals(resourceType)) {
            CCQuestionService questionService = SpringContextUtil.getBean(CCQuestionService.class);
            CTCQuestionDetailVO questionDetailVO = questionService.getQuestionDetailFromCache(resourceId, null);
            if (questionDetailVO != null) {
                return questionDetailVO.getQuestion().getAuthor();
            }
            return null;
        }
        if (ResourceTypeEnum.CTC_EXPERIENCE.getType().equals(resourceType)) {
            CCExperienceService experienceService = SpringContextUtil.getBean(CCExperienceService.class);
            CTCExperienceDetailVO experienceDetailVO = experienceService.getExperienceDetailFromCache(resourceId, null);
            if (experienceDetailVO != null) {
                return experienceDetailVO.getAuthor().getUserId();
            }
            return null;
        }
        if (ResourceTypeEnum.CTC_VIDEO.getType().equals(resourceType)) {
            CCVideoService videoService = SpringContextUtil.getBean(CCVideoService.class);
            CTCVideoDetailVO videoDetailVO = videoService.getQuestionDetailFromCache(resourceId, null);
            if (videoDetailVO != null) {
                return videoDetailVO.getVideo().getAuthor();
            }
            return null;
        }
        return null;
    }

    /**
     * 发送消息通知
     * @param userId
     * @param resourceId
     * @param resourceType
     */
    private void sendMessage(String userId, Long resourceId, String resourceType) {
        MessageConstants.TypeEnum typeEnum = ResourceBehaviorTypeEnum.getMessageType(behaviorType);
        if (typeEnum == null) {
            return;
        }
        String fromUserId = getResourceAuthor(resourceId, resourceType);
        if (StringUtils.isBlank(fromUserId)) {
            return;
        }
        // 通过代理模式获取用户展现消息队列服务
        UserMessageQueue messageQueue = SpringContextUtil.getBean(UserMessageQueue.class);
        // 构造消息参数
        LikeCollectMessageBuilder likeCollectMessageBuilder = new LikeCollectMessageBuilder();
        LikeCollectMessageDTO likeCollectMessageDTO = likeCollectMessageBuilder.build(typeEnum,
                                                                                      userId,
                                                                                      fromUserId,
                                                                                      ResourceTypeEnum.valueOf(resourceType),
                                                                                      resourceId.toString());
        // MQ 提交消息
        messageQueue.submit(likeCollectMessageDTO);
    }

     public Long getBehaviorCountFromCache(String resourceType, Long resourceId) {
         Map<Long, Long> map = getBehaviorCountFromCache(resourceType, Arrays.asList(resourceId));
         return map.getOrDefault(resourceId, 0L);
     }

     public Map<Long, Long> getBehaviorCountFromCache(String resourceType, List<Long> resourceIdList) {
         IResourceBehaviorDAO resourceBehaviorDAO = SpringContextUtil.getBean(IResourceBehaviorDAO.class);
         List<Map<String, Object>> list = resourceBehaviorDAO.countBehaviorByResourceList(resourceIdList, resourceType, behaviorType);
         Map<Long, Long> map = new HashMap<>();
         if (CollectionUtils.isEmpty(list)) {
            return map;
         }
         for (Map<String, Object> entry : list) {
             map.put(Long.valueOf(entry.get("rid").toString()), Long.valueOf(entry.get("ctn").toString()));
         }
         return map;
     }

    /***
     * 查询用户操作资源的数量
     * @param resourceType
     * @param userId
     * @return
     */
     public Long countUserBehaviorResource(String resourceType, String userId) {
         IResourceBehaviorDAO resourceBehaviorDAO = SpringContextUtil.getBean(IResourceBehaviorDAO.class);
         return resourceBehaviorDAO.countByUserIdAndResourceType(userId, resourceType);
     }

    public Map<Long, Boolean> getIsBehaviorFromCache(String resourceType, List<Long> resourceIdList, String userId) {
         if (StringUtils.isBlank(userId)) {
             return new HashMap<>();
         }
        IResourceBehaviorDAO resourceBehaviorDAO = SpringContextUtil.getBean(IResourceBehaviorDAO.class);
        List<ResourceBehaviorEntity> list = resourceBehaviorDAO.findAllByUserIdAndStatusAndResourceIndexInAndResourceTypeAndBehaviorType(userId, Boolean.TRUE, resourceIdList, resourceType, behaviorType);
        Map<Long, Boolean> map = new HashMap<>();
        if (CollectionUtils.isEmpty(list)) {
            return map;
        }
        for (ResourceBehaviorEntity entity : list) {
            map.put(entity.getResourceIndex(), true);
        }
        return map;
    }

    public boolean isBehaviorFromCache(String resourceType, Long resourceId, String userId) {
        Map<Long, Boolean> map = this.getIsBehaviorFromCache(resourceType, Arrays.asList(resourceId), userId);
        return ObjectUtils.equals(map.getOrDefault(resourceId, false), true);
    }

    public Map<Long, Map<String, Long>> getBehaviorUsersFromCache(String resourceType, List<Long> resourceIdList, int size) {
        RedisUtil RedisUtil = SpringContextUtil.getBean(RedisUtil.class);
        List<String> cacheKeys = resourceIdList.stream().map(id -> RedisKeyEnum.RESOURCE_BEHAVIOR_USERS_CACHE.getKey(behaviorType, resourceType, String.valueOf(id))).collect(Collectors.toList());
        Map<Long, Set<DefaultTypedTuple>> userMap = RedisUtil.zreverseRangeWithScoreByPipelined(cacheKeys, resourceIdList, 0, size);
        if (MapUtils.isEmpty(userMap)) {
            return null;
        }
        Map<Long, Map<String, Long>> userSupportResultMap = new LinkedHashMap<>();
        userMap.entrySet().forEach(entey -> {
            if (org.apache.commons.collections.CollectionUtils.isNotEmpty(entey.getValue())) {
                Map<String, Long> userNameAndScoreMap = new HashMap<>();
                entey.getValue().forEach(tuple -> userNameAndScoreMap.put(String.valueOf(tuple.getValue()),
                        tuple.getScore().longValue()));
                userSupportResultMap.put(entey.getKey(), userNameAndScoreMap);
            }
        });
        return userSupportResultMap;
    }

    /**
     * 查询资源的所有互动用户
     *
     * @param resourceType
     * @param resourceId
     * @return
     */
    public Set<String> getBehaviorUsersIdFromCache(String resourceType, Long resourceId) {
        RedisUtil RedisUtil = SpringContextUtil.getBean(RedisUtil.class);
        String key = RedisKeyEnum.RESOURCE_BEHAVIOR_USERS_CACHE.getKey(behaviorType, resourceType, String.valueOf(resourceId));
        if (!RedisUtil.exists(key)) {
            refreshBehaviorResourceUsersCache(resourceId, resourceType, behaviorType);
        }
        return RedisUtil.zrange(key, 0, Long.MAX_VALUE);
     }

    /**
     * 刷新行为数据缓存
     *
     * @param resourceId
     * @param resourceType
     * @param behaviorType
     * @return
     */
     public Long refreshBehaviorCache(Long resourceId, String resourceType, String behaviorType, String userId,
                                      boolean isCancel) {
         RedisUtil RedisUtil = SpringContextUtil.getBean(RedisUtil.class);
         Long scoreTime = DateUtils.getCurrentTimeMillis();
         String resourceIndex = String.valueOf(resourceId);
         /**
          * 刷新资源互动量
          */
         String resourceBehaviorCountKey = RedisKeyEnum.RESOURCE_BEHAVIOR_COUNT_CACHE.getKey(
                 behaviorType, resourceType, resourceIndex);
         if (!RedisUtil.exists(resourceBehaviorCountKey)) {
             this.refreshResourceBehaviorCountCache(resourceId, resourceType, behaviorType);
         } else {
             if (isCancel) {
                 RedisUtil.decrex(resourceBehaviorCountKey,
                         RedisKeyEnum.RESOURCE_BEHAVIOR_COUNT_CACHE.getExpireTime());
             } else {
                 RedisUtil.increx(resourceBehaviorCountKey,
                         RedisKeyEnum.RESOURCE_BEHAVIOR_COUNT_CACHE.getExpireTime());
             }
         }
         /**
          * 刷新资源互动的用户列表
          */
         String resourceBehaviorUsersKey = RedisKeyEnum.RESOURCE_BEHAVIOR_USERS_CACHE.getKey(behaviorType, resourceType, resourceIndex);
         if (!RedisUtil.exists(resourceBehaviorUsersKey)) {
             this.refreshBehaviorResourceUsersCache(resourceId, resourceType, behaviorType);
         } else {
             if (isCancel) {
                 RedisUtil.zremove(resourceBehaviorUsersKey, userId);
             } else {
                 RedisUtil.zadd(resourceBehaviorUsersKey, userId, scoreTime, RedisKeyEnum.RESOURCE_BEHAVIOR_USERS_CACHE.getExpireTime());
             }
         }
         /***
          * 刷新用户互动的资源列表
          */
         String userBehaviorResourceKey = RedisKeyEnum.USER_BEHAVIOR_RESOURCE_CACHE.getKey(behaviorType, resourceType, userId);
         if (!RedisUtil.exists(userBehaviorResourceKey)) {
             this.refreshUserBehaviorResourceCache(userId, resourceType, behaviorType);
         } else {
             if (isCancel) {
                 RedisUtil.zremove(userBehaviorResourceKey, resourceIndex);
             } else {
                 RedisUtil.zadd(userBehaviorResourceKey, resourceIndex, scoreTime, RedisKeyEnum.USER_BEHAVIOR_RESOURCE_CACHE.getExpireTime());
             }
         }
         return 0L;
     }

    /***
     * 取操作资源的用户个数
     * @return
     */
     public Long getBehaviorResultUserCountFromCache(String userId, String resourceType, String behaviorType) {

         return 0L;
     }

    public List<Long> getUserBehaviorResourceIdListFromCache(String userId, String resourceType, String behaviorType, int size) {
        List<String> list = this.getUserBehaviorResourceListFromCache(userId, resourceType, behaviorType, size);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        return list.stream().map(Long::valueOf).collect(Collectors.toList());
    }

    public List<String> getUserBehaviorResourceListFromCache(String userId, String resourceType, String behaviorType, int size) {
         RedisUtil RedisUtil = SpringContextUtil.getBean(RedisUtil.class);
        String userBehaviorResourceKey = RedisKeyEnum.USER_BEHAVIOR_RESOURCE_CACHE.getKey(behaviorType, resourceType, userId);
         if (!RedisUtil.exists(userBehaviorResourceKey)) {
             this.refreshUserBehaviorResourceCache(userId, resourceType, behaviorType);
         }
         Set<String> idSet = RedisUtil.zreverseRange(userBehaviorResourceKey, 0, size);
         return new ArrayList<>(idSet);
    }

    /***
     * 取用用户操作的资源个数
     * @return
     */
    public Long refreshResourceBehaviorCountCache(Long resourceId, String resourceType, String behaviorType) {
        RedisUtil RedisUtil = SpringContextUtil.getBean(RedisUtil.class);
        IResourceBehaviorDAO resourceBehaviorDAO = SpringContextUtil.getBean(IResourceBehaviorDAO.class);
        String resourceIndex = String.valueOf(resourceId);
        String resourceBehaviorCountKey = RedisKeyEnum.RESOURCE_BEHAVIOR_COUNT_CACHE.getKey(behaviorType,
                resourceType, resourceIndex);
        Long count = resourceBehaviorDAO.countAllByBehaviorTypeAndResourceTypeAndResourceIndexAndStatus(behaviorType,
                resourceType, resourceId, true);
        RedisUtil.set(resourceBehaviorCountKey, String.valueOf(count));
        return count;
    }

    /***
     * 刷新资源的互动用户列表
     * @param resourceId
     * @param resourceType
     * @param behaviorType
     * @return
     */
    public void refreshBehaviorResourceUsersCache(Long resourceId, String resourceType, String behaviorType) {
        RedisUtil RedisUtil = SpringContextUtil.getBean(RedisUtil.class);
        IResourceBehaviorDAO resourceBehaviorDAO = SpringContextUtil.getBean(IResourceBehaviorDAO.class);
        String resourceIndex = String.valueOf(resourceId);
        String resourceBehaviorUsersKey = RedisKeyEnum.RESOURCE_BEHAVIOR_USERS_CACHE.getKey(behaviorType, resourceType, resourceIndex);
        List<ResourceBehaviorEntity> resourceBehaviorEntities =
                resourceBehaviorDAO.findAllByResourceIndexAndResourceTypeAndBehaviorTypeAndStatus(resourceId, resourceType, behaviorType, true);
        if (CollectionUtils.isEmpty(resourceBehaviorEntities)) {
            return;
        }
        Set<ZSetOperations.TypedTuple<String>> tuples = resourceBehaviorEntities.stream().map(
                resourceBehaviorEntity -> {
                    String value = String.valueOf(resourceBehaviorEntity.getUserId());
                    Double score =
                            resourceBehaviorEntity.getUpdateDate() != null ?
                                    Double.valueOf(resourceBehaviorEntity.getUpdateDate().getTime()) :
                                    Double.valueOf(resourceBehaviorEntity.getCreateDate().getTime());
                    return new DefaultTypedTuple<>(value, score);
                }).collect(Collectors.toSet());
        RedisUtil.zadd(resourceBehaviorUsersKey, tuples, RedisKeyEnum.RESOURCE_BEHAVIOR_USERS_CACHE.getExpireTime());
    }

    /***
     * 刷新资源的互动用户列表
     * @param userId
     * @param resourceType
     * @param behaviorType
     * @return
     */
    public void refreshUserBehaviorResourceCache(String userId, String resourceType, String behaviorType) {
        RedisUtil RedisUtil = SpringContextUtil.getBean(RedisUtil.class);
        IResourceBehaviorDAO resourceBehaviorDAO = SpringContextUtil.getBean(IResourceBehaviorDAO.class);
        String userBehaviorResourceKey = RedisKeyEnum.USER_BEHAVIOR_RESOURCE_CACHE.getKey(behaviorType, resourceType, userId);
        List<ResourceBehaviorEntity> resourceBehaviorEntities =
                resourceBehaviorDAO.findAllByUserIdAndResourceTypeAndBehaviorTypeAndStatus(userId, resourceType, behaviorType, true);
        if (CollectionUtils.isEmpty(resourceBehaviorEntities)) {
            return;
        }
        Set<ZSetOperations.TypedTuple<String>> tuples = resourceBehaviorEntities.stream().map(
                resourceBehaviorEntity -> {
                    String value = String.valueOf(resourceBehaviorEntity.getResourceIndex());
                    Double score =
                            resourceBehaviorEntity.getUpdateDate() != null ?
                                    Double.valueOf(resourceBehaviorEntity.getUpdateDate().getTime()) :
                                    Double.valueOf(resourceBehaviorEntity.getCreateDate().getTime());
                    return new DefaultTypedTuple<>(value, score);
                }).collect(Collectors.toSet());
        RedisUtil.zadd(userBehaviorResourceKey, tuples, RedisKeyEnum.USER_BEHAVIOR_RESOURCE_CACHE.getExpireTime());
    }

    /**
     * 判断用户是否操作
     * @param resourceId
     * @param resourceType
     * @param behaviorType
     * @return
     */
    public Long isBehavior(Long resourceId, String resourceType, String behaviorType) {

        return 0L;
    }

    /**
     * 取用户操作的资源列表
     * @param userId
     * @param resourceType
     * @param behaviorType
     * @param lastBehaviorId
     * @param size
     * @return
     */
    public List<ResourceBehaviorResponseVO> getBehaviorUserListFromCache(String userId, String resourceType, String behaviorType, Long lastBehaviorId, int size) {

        return null;
    }

    /***
     * 取操作资源的用户列表
     * @param resourceId
     * @param resourceType
     * @param behaviorType
     * @param lastBehaviorId
     * @param size
     * @return
     */
    public List<ResourceBehaviorUserResponseVO> getBehaviorListByUserFromCache(Long resourceId, String resourceType, String behaviorType, Long lastBehaviorId, int size) {

        return null;
    }

    public Page<ResourceBehaviorEntity> page(String userId, String behaviorType, String resourceType, int pageNo, int pageSize) {
        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Order.desc("id")));
        IResourceBehaviorDAO resourceBehaviorDAO = SpringContextUtil.getBean(IResourceBehaviorDAO.class);
        return resourceBehaviorDAO.findAllByUserIdAndBehaviorTypeAndResourceTypeAndStatus(userId, behaviorType, resourceType, true, pageable);
    }
}
